package com.bingxue.edu.management.system.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil;
import com.bingxue.edu.common.enums.FileTypeEnum;
import com.bingxue.edu.framework.common.model.PageQuery;
import com.bingxue.edu.framework.common.util.CheckUtils;
import com.bingxue.edu.framework.common.util.ValidationUtils;
import com.bingxue.edu.framework.mybatis.base.BaseService;
import com.bingxue.edu.framework.mybatis.query.QueryBuilder;
import com.bingxue.edu.management.system.mapper.FileMapper;
import com.bingxue.edu.management.system.model.entity.File;
import com.bingxue.edu.management.system.model.entity.Storage;
import com.bingxue.edu.management.system.model.query.FileQuery;
import com.bingxue.edu.management.system.model.resp.FileResp;
import com.bingxue.edu.management.system.model.resp.FileStatisticsResp;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.ProgressListener;
import org.dromara.x.file.storage.core.upload.UploadPretreatment;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.bingxue.edu.management.system.model.entity.table.FileTableDef.FILE;
import static com.mybatisflex.core.query.QueryMethods.sum;

/**
* 文件业务实现类
*
* @author panbo
* @since 2024/07/24 08:57
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FileService extends BaseService<FileMapper, File> {

    private final StorageService storageService;
    private final FileStorageService fileStorageService;

    /**
     * 删除文件
     * @param ids 文件id列表
     */
    public void delete(List<String> ids) {
        List<File> fileList = this.getMapper().selectListByIds(ids);
        Map<String, List<File>> fileListGroup = fileList.stream().collect(Collectors.groupingBy(File::getStorageId));
        for (Map.Entry<String, List<File>> entry : fileListGroup.entrySet()) {
            Storage storage = storageService.getById(entry.getKey());
            for (File file : entry.getValue()) {
                FileInfo fileInfo = file.toFileInfo(storage.getCode());
                fileStorageService.delete(fileInfo);
            }
        }
        this.removeByIds(ids);
        log.info("删除文件成功, {}", ids);
    }

    /**
     * 上传到默认存储
     *
     * @param file 文件信息
     * @return 文件信息
     */
    public FileInfo upload(MultipartFile file) {
        return upload(file, null);
    }


    /**
     * 上传到指定存储
     *
     * @param file        文件信息
     * @param storageCode 存储编码
     * @return 文件信息
     */
    public FileInfo upload(MultipartFile file, String storageCode) {
        Storage storage;
        if (StrUtil.isBlank(storageCode)) {
            storage = storageService.getDefaultStorage();
            ValidationUtils.throwIfNull(storage, "请先指定默认存储");
        } else {
            storage = storageService.getByCode(storageCode);
            CheckUtils.throwIfNotExists(storage, "Storage", "Code", storageCode);
        }
        LocalDate today = LocalDate.now();
        String path = today.getYear() + StrUtil.SLASH + today.getMonthValue() + StrUtil.SLASH + today
                .getDayOfMonth() + StrUtil.SLASH;
        UploadPretreatment uploadPretreatment = fileStorageService.of(file)
                .setPlatform(storage.getCode())
                .putAttr(ClassUtil.getClassName(Storage.class, false), storage)
                .setPath(path);
        // 图片文件生成缩略图
        if (FileTypeEnum.IMAGE.getExtensions().contains(FileNameUtil.extName(file.getOriginalFilename()))) {
            uploadPretreatment.thumbnail(img -> img.size(100, 100));
        }
        uploadPretreatment.setProgressMonitor(new ProgressListener() {
            @Override
            public void start() {
                log.info("开始上传");
            }

            @Override
            public void progress(long progressSize, Long allSize) {
                log.info("已上传 [{}]，总大小 [{}]", progressSize, allSize);
            }

            @Override
            public void finish() {
                log.info("上传结束");
            }
        });
        // 处理本地存储文件 URL
        FileInfo fileInfo = uploadPretreatment.upload();
        String domain = StrUtil.appendIfMissing(storage.getDomain(), StrUtil.SLASH);
        fileInfo.setUrl(URLUtil.normalize(domain + fileInfo.getPath() + fileInfo.getFilename()));
        log.info("上传文件成功, {}", fileInfo);
        return fileInfo;
    }

    /**
     * 根据存储 ID 列表查询
     *
     * @param storageIds 存储 ID 列表
     * @return 文件数量
     */
    public Long countByStorageIds(List<String> storageIds) {
        return this.queryChain().where(FILE.STORAGE_ID.in(storageIds)).count();
    }

    /**
     * 查询文件资源统计信息
     *
     * @return 资源统计信息
     */
    public FileStatisticsResp statistics() {
        FileStatisticsResp resp = new FileStatisticsResp();
        List<FileStatisticsResp> statisticsList = this.queryChain()
                .select(FILE.FILE_TYPE.as(FileStatisticsResp::getType),
                        QueryMethods.count().as(FileStatisticsResp::getNumber),
                        sum(FILE.FILE_SIZE).as(FileStatisticsResp::getSize))
                .groupBy(FILE.FILE_TYPE)
                .listAs(FileStatisticsResp.class);
        if (CollUtil.isEmpty(statisticsList)) {
            return resp;
        }
        resp.setData(statisticsList);
        resp.setSize(statisticsList.stream().mapToLong(FileStatisticsResp::getSize).sum());
        resp.setNumber(statisticsList.stream().mapToLong(FileStatisticsResp::getNumber).sum());
        return resp;
    }

    /**
     * 填充信息
     * @param obj
     */
    public void fill(Object obj) {
        HashMap<String, Storage> storageMap = new HashMap<>(1);
        if (obj instanceof FileResp fileResp && !(HttpUtil.isHttp(fileResp.getFileUrl()) || HttpUtil.isHttps(fileResp.getFileUrl()))) {
            Storage storage = storageMap.computeIfAbsent(fileResp.getStorageId(), storageService::getById);
            String prefix = StrUtil.appendIfMissing(storage.getDomain(), StrUtil.SLASH);
            String url = URLUtil.normalize(prefix + fileResp.getFileUrl());
            fileResp.setFileUrl(url);
            String thumbnailUrl = StrUtil.isBlank(fileResp.getThumbnailUrl()) ? url : URLUtil.normalize(prefix + fileResp.getThumbnailUrl());
            fileResp.setThumbnailUrl(thumbnailUrl);
            fileResp.setStorageName("%s (%s)".formatted(storage.getName(), storage.getCode()));
        }
    }

    /**
     * 分页查询文件
     * @param fileQuery 文件查询条件
     * @param pageQuery 分页查询条件
     * @return 文件分页数据
     */
    public Page<FileResp> page(FileQuery fileQuery, PageQuery pageQuery) {
        QueryWrapper queryWrapper = QueryBuilder.build(fileQuery, pageQuery);
        Page<FileResp> page = pageAs(pageQuery.toPage(), queryWrapper, FileResp.class);
        page.getRecords().forEach(this::fill);
        return page;
    }
}
